#include "Animation.hpp"

using namespace std;

JOINT *JOINT::addJOINT(JOINT *child){
    if(childsSize < MAX_CHSIZE){
        childs[childsSize] = child;
        childsSize++;
        return child;
    } else {
        fprintf(stderr, "Childs tab %s is full\n", name);
        return NULL;
    }
    return child;
}

JOINT::JOINT(){
    childs = new JOINT*[MAX_CHSIZE];
    childsSize = 0;
}

JOINT::JOINT(char *_name, uint _num, vec3 _offset, JOINT *_parent){
    name = new char[20];
    num = _num;
    strcpy(name, _name);

    offset = _offset;
    parent = _parent;

    childs = new JOINT*[MAX_CHSIZE];
    childsSize = 0;
}

void JOINT::dumpTree(uint level){
    for (uint i = 0; i < level; i++){
        printf(" ");
    }
    printf("%s\n", name);

    for (uint i = 0; i < childsSize; i++)
        childs[i]->dumpTree(level + 1);
}

ROOT::ROOT(char *_name, vec3 _offset){
    name = new char[20];
    num = -1;
    strcpy(name, _name);

    offset = _offset;

    childs = new JOINT*[MAX_CHSIZE];
    childsSize = 0;

    parent = NULL;
}

ENDSITE::ENDSITE(uint _num, vec3 _offset, JOINT *_parent){
    num = _num;
    offset = _offset;
    parent = _parent;

    name = new char[20];
    strcpy(name, "ENDSITE");

    childsSize = 0;
}

void ENDSITE::dumpTree(uint level){
    for (uint i = 0; i < level; i++){
        printf(" ");
    }
    printf("End Site\n");
}

int BVH::loadHIERARCHY(FILE *file){
    char buffer[512];

    char segment[20];
    char name[20];
    char keyword[20];
    char brackets[40];

    char channel[6][20];

    int channelsNum;

    int channels = 0;

    vec3 tempVec3;

    fgets(buffer, 512, file);
    sscanf(buffer, "%s %s\n", segment, name);

    JOINT *temp;

    int actual_level = 1;

    if(!strcmp(segment, "ROOT")){

        fgets(buffer, 512, file);
        sscanf(buffer, "%s\n", brackets);
        if(!strcmp(brackets, "{")){

            fgets(buffer, 512, file);
            sscanf(buffer, "%s %f %f %f\n", keyword, &tempVec3.x, &tempVec3.y, &tempVec3.z);
            if(!strcmp(keyword, "OFFSET")){
                fgets(buffer, 512, file);
                sscanf(buffer, "%s %d %s  %s %s %s %s %s\n", keyword, &channelsNum, channel[0], channel[1], channel[2], channel[3], channel[4], channel[5]);
                if(!strcmp(keyword, "CHANNELS") &&
                   !strcmp(channel[0], "Xposition") &&
                   !strcmp(channel[1], "Yposition") &&
                   !strcmp(channel[2], "Zposition") &&
                   !strcmp(channel[3], "Zrotation") &&
                   !strcmp(channel[4], "Xrotation") &&
                   !strcmp(channel[5], "Yrotation") ){
                    //printf("%f %f %f\n", tempVec3.x, tempVec3.y, tempVec3.z);
                    root = new ROOT(name, tempVec3);
                    channels += 2;

                    temp = root;

                    fgets(buffer, 512, file);
                    while(actual_level){
                        sscanf(buffer, "%s  %s\n", segment, name);
                        while(strcmp(segment, "End")){
                            if(!strcmp(segment, "JOINT")){
                                fgets(buffer, 512, file);
                                sscanf(buffer, "%s\n", brackets);
                                if(!strcmp(brackets, "{")){

                                    fgets(buffer, 512, file);
                                    sscanf(buffer, "%s %f %f %f\n", keyword, &tempVec3.x, &tempVec3.y, &tempVec3.z);
                                    if(!strcmp(keyword, "OFFSET")){
                                        fgets(buffer, 512, file);
                                        sscanf(buffer, "%s %d %s  %s %s\n", keyword, &channelsNum, channel[0], channel[1], channel[2]);
                                        if(!strcmp(keyword, "CHANNELS") &&
                                           !strcmp(channel[3], "Zrotation") &&
                                           !strcmp(channel[4], "Xrotation") &&
                                           !strcmp(channel[5], "Yrotation") ){
                                            //printf("%f %f %f\n", tempVec3.x, tempVec3.y, tempVec3.z);
                                            temp = temp->addJOINT(new JOINT(name, bonesNum, tempVec3, temp));
                                            bonesNum++;
                                            channels += 1;
                                            actual_level++;

                                            fgets(buffer, 512, file);
                                            sscanf(buffer, "%s  %s\n", segment, name);
                                        } else {
                                            printf("wrong keyword: %s in %s\n", keyword, name);
                                            return 0;
                                        }
                                    } else {
                                        printf("wrong keyword: %s in %s\n", keyword, name);
                                        return 0;
                                    }
                                } else {
                                    printf("wrong buffer: %s in %s\n", brackets, name);
                                    return 0;
                                }
                            } else {
                                printf("segment: %s in %s\n", segment, name);
                                return 0;
                            }
                        }

                        fgets(buffer, 512, file);
                        sscanf(buffer, "%s\n", brackets);
                        if(!strcmp(brackets, "{")){
                            fgets(buffer, 512, file);
                            sscanf(buffer, "%s %f %f %f\n", keyword, &tempVec3.x, &tempVec3.y, &tempVec3.z);
                            if(!strcmp(keyword, "OFFSET")){
                                //printf("%f %f %f\n", tempVec3.x, tempVec3.y, tempVec3.z);
                                temp = temp->addJOINT(new ENDSITE(bonesNum, tempVec3, temp));
                                bonesNum++;
                                actual_level++;
                            }
                        }

                        fgets(buffer, 512, file);
                        sscanf(buffer, "%s\n", brackets);

                        while(!strcmp(brackets, "}")){
                            temp = temp->parent;
                            actual_level--;

                            fgets(buffer, 512, file);
                            sscanf(buffer, "%s\n", brackets);
                        }
                    }
                } else {
                    printf("wrong keyword: %s in %s\n", keyword, name);
                    return 0;
                }
            } else {
                printf("wrong keyword: %s in %s\n", keyword, name);
                return 0;
            }
        } else {
            printf("wrong buffer: %s in %s\n", brackets, name);
            return 0;
        }
    } else {
        printf("segment: %s in %s\n", segment, name);
        return 0;
    }

    if(!strcmp(brackets, "MOTION")){
        return channels;
    }
    return 0;
}

void JOINT::setChannel(vec3 **channelsTab, int *n){
    channel = channelsTab[*n];
    *n += 1;

    for (uint i = 0; i < childsSize; i++) childs[i]->setChannel(channelsTab, n);
}

void ROOT::setChannel(vec3 **channelsTab, int *n){
    position = channelsTab[*n];
    *n += 1;
    channel = channelsTab[*n];
    *n += 1;

    for (uint i = 0; i < childsSize; i++) childs[i]->setChannel(channelsTab, n);
}

void ENDSITE::setChannel(vec3 **channelsTab, int *n){
}

bool BVH::loadMOTION(FILE *file, int channels, uint frames){
    vec3 **channelsTab = new vec3*[channels];

    for(int i = 0; i < channels; i++){
        channelsTab[i] = new vec3[frames];
    }

    int n = 0;

    root->setChannel(channelsTab, &n);

    char buffer[1500];
    char tempString[20];

    fgets(buffer, 1500, file);

    vec3 tc;
    int len = 0;
    for(uint h = 0; h < frames; h++){
        len = 0;
        sscanf(buffer, "%f", &tc.x);
        sscanf(buffer, "%s ", tempString);
        len += strlen(tempString)+1;

        sscanf(buffer+len, "%f", &tc.y);
        sscanf(buffer+len, "%s ", tempString);
        len += strlen(tempString)+1;

        sscanf(buffer+len, "%f", &tc.z);
        sscanf(buffer+len, "%s ", tempString);
        len += strlen(tempString)+1;

        channelsTab[0][h] = tc;

        for(int i = 1; i < channels; i++){
            sscanf(buffer+len, "%f", &tc.z);
            sscanf(buffer+len, "%s ", tempString);
            len += strlen(tempString)+1;

            sscanf(buffer+len, "%f", &tc.x);
            sscanf(buffer+len, "%s ", tempString);
            len += strlen(tempString)+1;

            sscanf(buffer+len, "%f", &tc.y);
            sscanf(buffer+len, "%s ", tempString);
            len += strlen(tempString)+1;

            tc.x = radians(tc.x);
            tc.y = radians(tc.y);
            tc.z = radians(tc.z);

            channelsTab[i][h] = tc;
        }
        fgets(buffer, 1500, file);
    }

    return 0;
}

BVH::BVH(char *filepath){
    FILE *file;

    bonesNum = 0;

    char buffer[512];
    char part[2][20];

    int channels;

    if (!(file = fopen(filepath, "r")))
 	{
 		fprintf(stderr, "Can't open file %s for reading\n", filepath);
 	} else {
        fgets(buffer, 512, file);
        sscanf(buffer, "%s\n", part[0]);
        while(!feof(file)){
            if(!strcmp(part[0], "HIERARCHY")){
                channels = loadHIERARCHY(file);
                if(channels){
                    strcpy(part[0], "MOTION");
                }
            } else if(!strcmp(part[0], "MOTION")){
                uint f;
                fgets(buffer, 512, file);
                sscanf(buffer, "%s %d\n", part[0], &f);
                if(!strcmp(part[0], "Frames:")){
                    frames = f;
                } else {
                    printf("Wrong frames: %s\n", part[0]);
                    break;
                }

                float ft;
                fgets(buffer, 512, file);
                sscanf(buffer, "%s %s %f\n", part[0], part[1], &ft);
                if(!strcmp(part[0], "Frame") && !strcmp(part[1], "Time:")){
                    frameTime = ft;
                } else {
                    printf("Wrong frame time: %s %s\n", part[0], part[1]);
                    break;
                }

                loadMOTION(file, channels, frames);

                break;
            } else {

                fprintf(stderr, "PART: %s\n", part[0]);
                fprintf(stderr, "Wrong BVH part in file: %s\n", filepath);
                break;

            }
        }
 	}
}

void BVH::dumpTree(){
    root->dumpTree(0);
}
